<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <style>
      /* 填写样式 */
    </style>
  </head>

  <body>
    <!-- 填写标签 -->
    <script type="text/javascript">
      // 填写JavaScript
      /**
       * 一道柯里化的题，受题解区一位大佬写法的启发，这里会在解析他的巧法的同时也扩展更多写法
       * 这里有个比较少见的点，函数的length属性可以表示已赋值以外所需的参数的个数
       */

      /**
       * 为了方便大家理解，我们先来解析一下那位大佬的写法
       * 先抛开if判断不管，通过闭包保证fn函数被存储起来，递归的过程中使用fn存储新参数,
       * 也就是说fn的柯里化传参已经实现了，还差一个停下来返回的条件，
       * 题目怎么说的，一个参数的情况直接返回，多个参数的情况才柯里化，此言差矣，一个参数的情况就不能看作柯里化的极限情况吗?
       * 这里我们使用函数的leng属性判断还剩几个参数没有赋值也就是还有几个参数需要传递，当只剩下一个参数未传递时直接返回fn不就行了
       */

      //   function curryIt(fn) {
      //     return function fun(a) {
      //       if (fn.length === 1) return fn(a); // 如果 fn 需要的参数个数是 1，直接执行并返回结果
      //       fn = fn.bind(null, a); // 否则，将当前参数 a 绑定到 fn，返回一个新的函数
      //       return fun; // 返回 fun 函数本身，继续等待下一个参数
      //     };
      //   }

      //难道秉承多一句不如少一句的优良传统，咱缩一下，主打一个防御性编程
      // const curryIt = (fn) => (a) =>
      //   fn.length === 1 ? fn(a) : curryIt(fn.bind(null, a));

      /**
       * 一小友肯定发现了虽然实现了这题，但是柯里化每次都只能接受一个参数吗，
       * 难道不能一次接受多个参数吗，答案是肯定的，那是不是得安排一下
       * 整个展开符传参再改下终止条件，小小柯里化拿下拿下
       */
      const curryIt = (fn) => (...args) => {
          if(args.length >= fn.length) return fn(...args);
          return curryIt(fn.bind(null, ...args));
      };

      /**
       * 有bind的地方怎么能少了apply和call的写法呢，安排一下
       * apply写法就和bind有本质的差别了，bind是创建一个新函数，所以我们能使用fn一直套参数，curryIt只负责递归，
       * 但是apply是直接调用fn，没法自己存参数，得把这活丢给curryIt，每次apply唤起curryIt顺便传参，眨眼睛给自己也绕的有点晕🤣🤣🤣
       * call的写法就很相似了，区别只是apply需要数组，call只是普通参数传参,传参过程大同小异，只需要保证最后fn拿到的是参数数组即可
       * 就到这了，有点tired，手动模拟的解决方法咱就暂时不写了，感兴趣的小友可自行尝试
       */

      //apply的写法
      // const curryIt = (fn) => (...args) =>
      //     args.length >= fn.length ? fn.apply(null, args)
      //     : (...moreArgs) => curryIt(fn).apply(null, [...args, ...moreArgs]);

      //call的写法
      // const curryIt = (fn) => (...args) =>
      //       args.length >= fn.length ? fn.call(null, ...args)
      //       : (...moreArgs) => curryIt(fn).call(null, ...args, ...moreArgs);

      function sum(a, b, c) {
        return a + b + c;
      }
      const curriedSum = curryIt(sum);

      //以上解题方法前两个是不支持每次传输不定参数数量的，小友测试时需注意！！！
      console.log(curriedSum(1)(2)(3)); // 输出 6
    </script>
  </body>
</html>
